#pragma once

#include <cppunit/config/SourcePrefix.h>
#include <iostream>
#include <string>
#include <algorithm>
#include "TestEntityTable.h"

void EntityTableTest::setUp(){

	validEntityInput.push_back(Type::Entity::ASSIGNMENT_LINE_ENTITY);
	validEntityInput.push_back(Type::Entity::IF_LINE_ENTITY);
	validEntityInput.push_back(Type::Entity::WHILE_LINE_ENTITY);
	validEntityInput.push_back(Type::Entity::CALL_LINE_ENTITY);
	validEntityInput.push_back(Type::Entity::ASSIGNMENT_LINE_ENTITY);
	validEntityInput.push_back(Type::Entity::IF_LINE_ENTITY);
	validEntityInput.push_back(Type::Entity::WHILE_LINE_ENTITY);
	validEntityInput.push_back(Type::Entity::CALL_LINE_ENTITY);
	validEntityInput.push_back(Type::Entity::ASSIGNMENT_LINE_ENTITY);
	validEntityInput.push_back(Type::Entity::IF_LINE_ENTITY);

	invalidEntityInput.push_back(Type::Entity::INVALID_LINE_ENTITY);

	validLineInput.push_back(1);
	validLineInput.push_back(2);
	validLineInput.push_back(3);
	validLineInput.push_back(5);
	validLineInput.push_back(6);
	validLineInput.push_back(7);
	validLineInput.push_back(8);
	validLineInput.push_back(9);
	validLineInput.push_back(10);
	validLineInput.push_back(11);

	invalidLineInput.push_back(0);

	nonExistLineInput.push_back(4);

	entityTb = EntityTable();
}

void EntityTableTest::tearDown(){
	validEntityInput.clear();
	invalidEntityInput.clear();

	validLineInput.clear();
	invalidLineInput.clear();
	nonExistLineInput.clear();
}

// Registers the fixture into the 'registry'
CPPUNIT_TEST_SUITE_REGISTRATION( EntityTableTest );

void EntityTableTest::testInsert(Type::List::Line lineList, Type::List::LineEntity entityList, 
	bool pass, std::string failMsg){
		Type::Index::Line tmpLine;
		Type::Entity::Line tmpEntity;
		unsigned int t, testNumber;
		bool tmpTF;

		if(lineList.size() > entityList.size()){
				testNumber = lineList.size();
		}else{
			testNumber = entityList.size();
		}

		// verify insertion of invalid line, valid entity, constant will always return false;
		for(t =0 ; t < testNumber; t++){
			tmpLine = lineList.at(Utilities::getIndexBoundedBySize(t, lineList.size()));
			tmpEntity = entityList.at(Utilities::getIndexBoundedBySize(t, entityList.size()));

			tmpTF = entityTb.insertLineEntity(tmpLine, tmpEntity);

			if(!pass){
				tmpTF = !tmpTF;
			}

			CPPUNIT_ASSERT_ASSERTION_PASS_MESSAGE(failMsg, CPPUNIT_ASSERT(tmpTF));
		}
}

void EntityTableTest::testGetEntities(Type::Index::Line line, bool isLinePresent, 
	Type::List::LineEntity entityList, std::string failMsg){
		Type::List::LineEntity tmpEntityList, bufEntityList;
		Type::Entity::Line bufEntity;
		unsigned int t;
		bool tmpTF;

		if(isLinePresent){
			tmpEntityList.push_back(entityTb.getLineEntityByLine(line));
		}else{
			tmpEntityList = entityTb.getLineEntities();
		}

		tmpTF = entityList.size() == tmpEntityList.size();
		CPPUNIT_ASSERT_ASSERTION_PASS_MESSAGE(failMsg + " (size different)", 
			CPPUNIT_ASSERT(tmpTF));

		for(t = 0; t< entityList.size();t++){
			bufEntity = entityList.at(t);

			bufEntityList = Utilities::removeElementFromList(tmpEntityList, bufEntity, true);
			tmpTF = bufEntityList.size() < tmpEntityList.size();
			CPPUNIT_ASSERT_ASSERTION_PASS_MESSAGE(failMsg + " (missing entity)", 
				CPPUNIT_ASSERT(tmpTF));

			tmpEntityList = bufEntityList;
		}

		CPPUNIT_ASSERT_ASSERTION_PASS_MESSAGE(failMsg + " (extra entity)", 
			CPPUNIT_ASSERT(tmpEntityList.empty()));
}

void EntityTableTest::testGetLines(Type::Entity::Line entity, bool isEntityPresent, 
	Type::List::Line lineList, std::string failMsg){
		Type::List::Line tmpLineList, bufLineList;
		Type::Index::Line bufLine;
		unsigned int t;
		bool tmpTF;

		if(isEntityPresent){
			tmpLineList = entityTb.getLinesByEntity(entity);
		}else{
			tmpLineList = entityTb.getLines();
		}

		tmpTF = lineList.size() == tmpLineList.size();

		CPPUNIT_ASSERT_ASSERTION_PASS_MESSAGE(failMsg + " (size different)", 
			CPPUNIT_ASSERT(tmpTF));

		for(t = 0; t< lineList.size();t++){
			bufLine = lineList.at(t);

			bufLineList = Utilities::removeElementFromList(tmpLineList, bufLine, true);
			tmpTF = bufLineList.size() < tmpLineList.size();
			CPPUNIT_ASSERT_ASSERTION_PASS_MESSAGE(failMsg + " (missing line)", 
				CPPUNIT_ASSERT(tmpTF));

			tmpLineList = bufLineList;
		}

		CPPUNIT_ASSERT_ASSERTION_PASS_MESSAGE(failMsg + " (extra line)", 
			CPPUNIT_ASSERT(tmpLineList.empty()));
}

void EntityTableTest::testIsLineEntity(Type::List::Line lineList, Type::List::LineEntity entityList, 
	bool pass, std::string failMsg){
		Type::Index::Line tmpLine;
		Type::Entity::Line tmpEntity;
		unsigned int t;
		bool tmpTF;

		for(t =0 ; t < lineList.size(); t++){
			tmpLine = lineList.at(t);

			// invalid or non existing
			if(entityList.size() != lineList.size()){
				tmpEntity = Type::Entity::INVALID_LINE_ENTITY;
			}else{
				tmpEntity = entityList.at(Utilities::getIndexBoundedBySize(t, entityList.size()));
			}

			tmpTF = entityTb.isLineEntity(tmpLine, tmpEntity);

			if(!pass){
				tmpTF = !tmpTF;
			}

			CPPUNIT_ASSERT_ASSERTION_PASS_MESSAGE(failMsg ,CPPUNIT_ASSERT(tmpTF));
		}
}
	
void EntityTableTest::testIsExists(Type::List::Line lineList, Type::List::LineEntity entityList, 
	bool pass, std::string failMsg){
		Type::Index::Line tmpLine;
		Type::Entity::Line tmpEntity;
		unsigned int t;
		bool tmpTF;

		for(t =0 ; t < lineList.size(); t++){
			tmpLine = lineList.at(t);
			tmpTF = entityTb.isLineExistsInEntity(tmpLine);

			if(!pass){
				tmpTF = !tmpTF;
			}

			CPPUNIT_ASSERT_ASSERTION_PASS_MESSAGE(failMsg + " (line existence)" ,CPPUNIT_ASSERT(tmpTF));
		}
		
		for(t =0 ; t < entityList.size(); t++){
			tmpEntity = entityList.at(t);
			tmpTF = entityTb.isEntityExistsInLine(tmpEntity);

			if(!pass){
				tmpTF = !tmpTF;
			}
			CPPUNIT_ASSERT_ASSERTION_PASS_MESSAGE(failMsg + " (entity existence)" ,CPPUNIT_ASSERT(tmpTF));
		}
}

// method to test the insertion of line, entity
void EntityTableTest::testInsert(){
	std::cout<<std::endl<<"EntityTable Insert()"<<std::endl;

	// verify insertion of invalid line, valid entity, constant will always return false;
	msg = "Failed at insertion of invalid line, valid entity";
	testInsert(invalidLineInput, validEntityInput, false, msg);

	// verify insertion of invalid entity, valid line, constant will always return false;
	msg = "Failed at insertion of invalid entity, valid line";
	testInsert(validLineInput, invalidEntityInput, false, msg);

	// verify insertion of valid line, entity will always return true;
	msg = "Failed at insertion of valid line, entity";
	testInsert(validLineInput, validEntityInput, true, msg);

	// verify insertion of repeated valid line, entity will always return false;
	msg = "Failed at insertion of valid line, entity";
	testInsert(validLineInput, validEntityInput, false, msg);
}

// method to test the retrieval of line, entity
void EntityTableTest::testGet(){
	std::cout<<std::endl<<"EntityTable Get()"<<std::endl;

	Type::List::Line bufLineList;
	Type::List::LineEntity bufEntityList;

	Type::Entity::Line tmpEntity;
	Type::Index::Line tmpLine;
	unsigned int t;

	testInsert();

	// verify all entities retrival will always correct;
	bufEntityList = Utilities::copyListWithoutDuplicates(validEntityInput);
	msg = "Failed at getting all entities";
	testGetEntities(Type::Entity::INVALID_LINE_ENTITY, false, bufEntityList, msg);

	// verify entity retrival of valid line will always correct;
	bufLineList = Utilities::copyListWithoutDuplicates(validLineInput);
	for(t =0 ; t < bufLineList.size(); t++){
		tmpLine = bufLineList.at(t);

		bufEntityList = Utilities::copyListWithValueInSource(validLineInput, tmpLine, validEntityInput);
		bufEntityList = Utilities::copyListWithoutDuplicates(bufEntityList);
		
		msg = "Failed at getting entity of valid line";
		testGetEntities(tmpLine, true, bufEntityList, msg);
	}

	// verify all lines retrival will always correct;
	bufLineList = Utilities::copyListWithoutDuplicates(validLineInput);
	msg = "Failed at getting all lines (with all line entity type)";
	testGetLines(Type::Entity::ALL_LINE_ENTITY, true, bufLineList, msg);
	msg = "Failed at getting all lines (without all line entity type)";
	testGetLines(Type::Entity::INVALID_LINE_ENTITY, false, bufLineList, msg);

	// verify lines retrival of valid entity will always correct;
	bufEntityList = Utilities::copyListWithoutDuplicates(validEntityInput);
	for(t =0 ; t < bufEntityList.size(); t++){
		tmpEntity = bufEntityList.at(t);

		bufLineList = Utilities::copyListWithValueInSource(validEntityInput, tmpEntity, validLineInput);
		bufLineList = Utilities::copyListWithoutDuplicates(bufLineList);
		
		msg = "Failed at getting lines of valid entity";
		testGetLines(tmpEntity, true, bufLineList, msg);
	}
}

// method to test the correctness of line, entity records
void EntityTableTest::testIsLineEntity(){
	std::cout<<std::endl<<"EntityTable isLineEntity()"<<std::endl;

	testInsert();

	// verify correctness of inserted valid line, entity
	msg = "Failed checking correctness of inserted line, constant";
	testIsLineEntity(validLineInput, validEntityInput, true, msg);

	// verify correctness of not inserted line, entity
	msg = "Failed checking correctness of not inserted line, constant";
	testIsLineEntity(nonExistLineInput, std::vector<Type::Entity::Line>() , false, msg);

	// verify correctness of invalid line, entity
	msg = "Failed checking correctness of invalid line, constant";
	testIsLineEntity(invalidLineInput, invalidEntityInput, false, msg);
}

// method to test the existence of line, entity
void EntityTableTest::testIsExists(){
	std::cout<<std::endl<<"EntityTable isExist()"<<std::endl;

	testInsert();

	// verify existence of inserted valid line, entity
	msg = "Failed checking existence of inserted line, constant";
	testIsExists(validLineInput, validEntityInput, true, msg);

	// verify existence of not inserted line, entity
	msg = "Failed checking existence of not inserted line, constant";
	testIsExists(nonExistLineInput, std::vector<Type::Entity::Line>() , false, msg);

	// verify existence of invalid line, entity
	msg = "Failed checking existence of invalid line, constant";
	testIsExists(invalidLineInput, invalidEntityInput, false, msg);
}